/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2008 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

package de.breitbeil.samsyncro;

import java.io.Serializable;

import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.TitledBorder;

import org.apache.commons.lang.StringUtils;

import com.funambol.framework.engine.source.ContentType;
import com.funambol.framework.engine.source.SyncSourceInfo;

import com.funambol.admin.AdminException;
import com.funambol.admin.ui.SourceManagementPanel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


/**
 * This class implements the configuration panel for MySyncSource
 *
 */
public class MySyncSourceAdminPanel
extends SourceManagementPanel
implements Serializable {

    // --------------------------------------------------------------- Constants

    /**
     * Allowed characters for name and uri
     */
    public static final String NAME_ALLOWED_CHARS
    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.";

    // ------------------------------------------------------------ Private data
    /** label for the panel's name */
    private JLabel panelName = new JLabel();

    /** border to evidence the title of the panel */
    private TitledBorder  titledBorder1;

    private JLabel           nameLabel          = new JLabel()     ;
    private JTextField       nameValue          = new JTextField() ;
    private JLabel           typeLabel          = new JLabel()     ;
    private JTextField       typeValue          = new JTextField() ;
    private JLabel           versionLabel       = new JLabel()     ;
    private JTextField       versionValue       = new JTextField() ;

    private JLabel           sourceUriLabel     = new JLabel()     ;
    private JTextField       sourceUriValue     = new JTextField() ;
    
    private JLabel           myStringLabel      = new JLabel()     ;
    private JTextField       myStringValue      = new JTextField() ;
    
    private JLabel           myIntLabel         = new JLabel()     ;
    private JTextField       myIntValue         = new JTextField() ;
    
    private JLabel           myMapLabel         = new JLabel()     ;
    private JTextField       myMapEntry         = new JTextField() ;
    private JTextField       myMapValue         = new JTextField() ;

    private JButton          confirmButton      = new JButton()    ;

    private MySyncSource     syncSource         = null             ;

    // ------------------------------------------------------------ Constructors

    /**
     * Creates a new MySyncSourceAdminPanel instance
     */
    public MySyncSourceAdminPanel() {
        init();
    }

    // ----------------------------------------------------------- Private methods

    /**
     * Create the panel
     * @throws Exception if error occures during creation of the panel
     */
    private void init(){
        // set layout
        this.setLayout(null);

        // set properties of label, position and border
        //  referred to the title of the panel
        titledBorder1 = new TitledBorder("");

        panelName.setFont(titlePanelFont);
        panelName.setText("Edit My SyncSource");
        panelName.setBounds(new Rectangle(14, 5, 316, 28));
        panelName.setAlignmentX(SwingConstants.CENTER);
        panelName.setBorder(titledBorder1);

        int y  = 60;
        int dy = 30;

        sourceUriLabel.setText("Source URI: ");
        sourceUriLabel.setFont(defaultFont);
        sourceUriLabel.setBounds(new Rectangle(14, y, 150, 18));
        sourceUriValue.setFont(defaultFont);
        sourceUriValue.setBounds(new Rectangle(170, y, 350, 18));

        y += dy;

        nameLabel.setText("Name: ");
        nameLabel.setFont(defaultFont);
        nameLabel.setBounds(new Rectangle(14, y, 150, 18));
        nameValue.setFont(defaultFont);
        nameValue.setBounds(new Rectangle(170, y, 350, 18));

        y += dy;

        typeLabel.setText("Supported type: ");
        typeLabel.setFont(defaultFont);
        typeLabel.setBounds(new Rectangle(14, y, 150, 18));
        typeValue.setFont(defaultFont);
        typeValue.setBounds(new Rectangle(170, y, 350, 18));

        y += dy;

        versionLabel.setText("Supported version: ");
        versionLabel.setFont(defaultFont);
        versionLabel.setBounds(new Rectangle(14, y, 150, 18));
        versionValue.setFont(defaultFont);
        versionValue.setBounds(new Rectangle(170, y, 350, 18));
        
        y += dy;

        myStringLabel.setText("MyString: ");
        myStringLabel.setFont(defaultFont);
        myStringLabel.setBounds(new Rectangle(14, y, 150, 18));
        myStringValue.setFont(defaultFont);
        myStringValue.setBounds(new Rectangle(170, y, 350, 18));
        
        y += dy;

        myIntLabel.setText("MyInt: ");
        myIntLabel.setFont(defaultFont);
        myIntLabel.setBounds(new Rectangle(14, y, 150, 18));
        myIntValue.setFont(defaultFont);
        myIntValue.setBounds(new Rectangle(170, y, 35, 18));
        
        y += dy;

        myMapLabel.setText("MyMap entry: ");
        myMapLabel.setFont(defaultFont);
        myMapLabel.setBounds(new Rectangle(14, y, 150, 18));
        myMapEntry.setFont(defaultFont);
        myMapEntry.setBounds(new Rectangle(170, y, 35, 18));
        myMapValue.setFont(defaultFont);
        myMapValue.setBounds(new Rectangle(210, y, 35, 18));

        y += dy;
        y += dy;

        confirmButton.setFont(defaultFont);
        confirmButton.setText("Add");
        confirmButton.setBounds(170, y, 70, 25);

        confirmButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event ) {
                try {
                    validateValues();
                    getValues();
                    if (getState() == STATE_INSERT) {
                        MySyncSourceAdminPanel.this.actionPerformed(
                            new ActionEvent(MySyncSourceAdminPanel.this,
                                            ACTION_EVENT_INSERT,
                                            event.getActionCommand()));
                    } else {
                        MySyncSourceAdminPanel.this.actionPerformed(
                            new ActionEvent(MySyncSourceAdminPanel.this,
                                            ACTION_EVENT_UPDATE,
                                            event.getActionCommand()));
                    }
                } catch (Exception e) {
                    notifyError(new AdminException(e.getMessage()));
                }
            }
        });

        // add all components to the panel
        this.add(panelName        , null);
        this.add(nameLabel        , null);
        this.add(nameValue        , null);
        this.add(typeLabel        , null);
        this.add(typeValue        , null);
        this.add(versionLabel     , null);
        this.add(versionValue     , null);
        this.add(sourceUriLabel   , null);
        this.add(sourceUriValue   , null);
        this.add(myStringLabel    , null);
        this.add(myStringValue    , null);
        this.add(myIntLabel       , null);
        this.add(myIntValue       , null);
        this.add(myMapLabel       , null);
        this.add(myMapEntry       , null);
        this.add(myMapValue       , null);
        this.add(confirmButton    , null);

    }

    /**
     * Loads the given syncSource showing the name, uri and type in the panel's
     * fields.
     *
     * @param syncSource the SyncSource instance
     */
    public void updateForm() {
         if (!(getSyncSource() instanceof MySyncSource)) {
          notifyError(
              new AdminException(
                  "This is not an MySyncSource! Unable to process SyncSource values."
              )
          );
          return;
        }
        if (getState() == STATE_INSERT) {
          confirmButton.setText("Add");
        } else if (getState() == STATE_UPDATE) {
          confirmButton.setText("Save");
        }

        this.syncSource = (MySyncSource) getSyncSource();

        sourceUriValue.setText(syncSource.getSourceURI() );
        nameValue.setText     (syncSource.getName()      );

        SyncSourceInfo info = syncSource.getInfo();        
        if (info != null) {
            ContentType[] types = info.getSupportedTypes();

            StringBuffer typesList = new StringBuffer(), versionsList = new StringBuffer();
            for (int i=0; ((types != null) && (i<types.length)); ++i) {
                typesList.append(types[i].getType());
                versionsList.append(types[i].getVersion());
                //
                // Also if the SyncSourceInfo contains more contentTypes, we handle
                // just the first one
                //
                break;
            }

            typeValue.setText(typesList.toString());
            versionValue.setText(versionsList.toString());
        }

        if (this.syncSource.getSourceURI() != null) {
            sourceUriValue.setEditable(false);
        }
        
        if (syncSource.getMyString() != null) {
            myStringValue.setText(syncSource.getMyString());
        } else {
            myStringValue.setText("");
        }
        myIntValue.setText(String.valueOf(syncSource.getMyInt()));
        
        Map map = syncSource.getMyMap();
        if (map != null) {
            Iterator i = map.keySet().iterator(); 
            if (i.hasNext()) {
                String key = (String)i.next();
                myMapEntry.setText(key);
                Object value = map.get(key);
                myMapValue.setText(((value != null) ? (String)value : ""));
            }
        }
    }

    // ----------------------------------------------------------- Private methods
    /**
     * Checks if the values provided by the user are all valid. In caso of errors,
     * a IllegalArgumentException is thrown.
     *
     * @throws IllegalArgumentException if:
     *         <ul>
     *         <li>name, uri, type or directory are empty (null or zero-length)
     *         <li>the types list length does not match the versions list length
     *         </ul>
     */
    private void validateValues() throws IllegalArgumentException {
        String value = null;

        value = nameValue.getText().trim();
        if (StringUtils.isEmpty(value)) {
            throw new
            IllegalArgumentException(
            "Field 'Name' cannot be empty. Please provide a SyncSource name.");
        }

        if (!StringUtils.containsOnly(value, NAME_ALLOWED_CHARS.toCharArray())) {
            throw new
            IllegalArgumentException(
            "Only the following characters are allowed for field 'Name': \n" + NAME_ALLOWED_CHARS);
        }

        value = typeValue.getText().trim();
        if (StringUtils.isEmpty(value)) {
            throw new
            IllegalArgumentException(
            "Field 'Supported Type' cannot be empty. Please provide a type.");
        }

        value = versionValue.getText().trim();
        if (StringUtils.isEmpty(value)) {
            throw new
            IllegalArgumentException(
            "Field 'Supported Version' cannot be empty. Please provide a version.");
        }

        value = sourceUriValue.getText().trim();
        if (StringUtils.isEmpty(value)) {
            throw new
            IllegalArgumentException(
            "Field 'Source URI' cannot be empty. Please provide a SyncSource URI.");
        }
        
        value = myIntValue.getText().trim();
        try {
            Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new
            IllegalArgumentException(
            "Field 'myInt' must be an integer number.");
        }
        
        value = myMapValue.getText().trim();
        if (!StringUtils.isEmpty(value) && StringUtils.isEmpty(myMapEntry.getText())) {
            throw new
            IllegalArgumentException(
            "Missing key for 'myMap'.");
        }
    }

    /**
     * Set syncSource properties with the values provided by the user.
     */
    private void getValues() {
        syncSource.setSourceURI(sourceUriValue.getText().trim());
        syncSource.setName     (nameValue.getText().trim()     );

        ContentType[] contentTypes = new ContentType[] {
            new ContentType(typeValue.getText(), versionValue.getText())
        };

        syncSource.setInfo(new SyncSourceInfo(contentTypes, 0));
        
        syncSource.setMyString(myStringValue.getText().trim());
        syncSource.setMyInt(Integer.parseInt(myIntValue.getText().trim()));
        
        HashMap map = new HashMap();
        map.put(myMapEntry.getText().trim(), myMapValue.getText().trim());
        syncSource.setMyMap(map);
    }
}